package com.hui.stock.service.impl;

import com.google.common.collect.Lists;
import com.hui.stock.config.TaskExecutePoolConfig;
import com.hui.stock.constant.ParseType;
import com.hui.stock.face.StockCacheFace;
import com.hui.stock.mapper.StockBlockRtInfoMapper;
import com.hui.stock.mapper.StockBusinessMapper;
import com.hui.stock.mapper.StockMarketIndexInfoMapper;
import com.hui.stock.mapper.StockRtInfoMapper;
import com.hui.stock.pojo.entity.StockBlockRtInfo;
import com.hui.stock.pojo.entity.StockMarketIndexInfo;
import com.hui.stock.pojo.entity.StockRtInfo;
import com.hui.stock.pojo.vo.StockInfoConfig;
import com.hui.stock.service.StockTimerTaskService;
import com.hui.stock.utils.DateTimeUtil;
import com.hui.stock.utils.IdWorker;
import com.hui.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.spring.web.json.Json;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author: ymh
 * @date: 2024/12/9 17:38
 * @description:
 */
@Service("stockTimerTaskServiceImpl")
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private StockCacheFace stockCacheFace;

    /**
     * 获取国内大盘实时数据
     */
    @Override
    public void getInnerMarketInfo() {
        //1 从api获取数据
        //1.1 获取数据地址
        String url = stockInfoConfig.getMarketUrl() + String.join(",",stockInfoConfig.getInner());
        //1.2 设置请求头
        HttpHeaders headers = new HttpHeaders();
        //1.3 防盗链
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0");
        //1.4 封装请求实体对象
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        //1.5 TODO 采集数据(获取所有响应参数)
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        int statusCode = exchange.getStatusCodeValue();
        if (statusCode!=200){
            log.error("当前时间：{},采集数据失败，状态码：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),statusCode);
            return;
        }
        //2 处理js数据
        String jsData = exchange.getBody();
        List<StockMarketIndexInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.INNER);
        log.info("采集的当前大盘数据：{}",list);
        //批量插入
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        //3 保存数据
        int count = stockMarketIndexInfoMapper.insertBatch(list);
        if (count>0){
            log.info("当前时间：{},插入数据{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
            //通知后台终端刷新本地缓存，发送的日期数据是告知对方当前更新的股票数据所在时间点
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
        }else {
            log.error("当前时间：{},插入数据{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
        }
    }

    /**
     * 批量获取个股分时数据详情信息
     * http://hq.sinajs.cn/list=sz000002,sh600015
     */
    @Override
    public void getStockRtIndex() {
        //1 从api获取数据
        //1.2 设置请求头
        HttpHeaders headers = new HttpHeaders();
        //1.3 防盗链
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0");
        // //1.4 获取个股编码
        // List<String> stockCodes = stockBusinessMapper.getAllStockCodes();
        // //1.5 将个股编码分组
        // List<String> collect = stockCodes.stream().map(code -> code.startsWith("6") ? "sh" + code : "sz" + code).collect(Collectors.toList());
        List<String> collect = stockCacheFace.getAllStockCodeWithPredix();
        List<List<String>> lists = Lists.partition(collect, 20);
        //1.6 分组获取数据
        long startTime = System.currentTimeMillis();
        for (List<String> list : lists) {
            // //1.1 获取数据地址
            // String url = stockInfoConfig.getMarketUrl()+String.join(",", list);
            // HttpEntity<Object> entity = new HttpEntity<>(headers);
            // //TODO 获取响应数据(直接获取响应数据)
            // String result = restTemplate.postForObject(url,entity,String.class);
            // //2 处理js数据
            // List<StockRtInfo> info = parserStockInfoUtil.parser4StockOrMarketInfo(result, ParseType.ASHARE);
            // log.info("采集的当前个股数据：{}",info);
            // //批量插入
            // if (CollectionUtils.isEmpty(info)) {
            //     return;
            // }
            // //3 保存
            // int count = stockRtInfoMapper.insertBatch(info);
            // if (count>0){
            //     log.info("当前时间：{},插入数据{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
            // }else {
            //     log.error("当前时间：{},插入数据{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
            // }
            //方案1：多线程:原始方案是将集合分片，然后分批次串行采集数据，效率不高，有较高的延迟，所以用多线程
            //但是也有问题：每次来任务，就会创建一个新线程，复用性差；可能会造成cpu竞争激烈
            // new Thread(() -> {
            //     //1.1 获取数据地址
            //     String url = stockInfoConfig.getMarketUrl()+String.join(",", list);
            //     HttpEntity<Object> entity = new HttpEntity<>(headers);
            //     //TODO 获取响应数据(直接获取响应数据)
            //     String result = restTemplate.postForObject(url,entity,String.class);
            //     //2 处理js数据
            //     List<StockRtInfo> info = parserStockInfoUtil.parser4StockOrMarketInfo(result, ParseType.ASHARE);
            //     log.info("采集的当前个股数据：{}",info);
            //     //批量插入
            //     if (CollectionUtils.isEmpty(info)) {
            //         return;
            //     }
            //     //3 保存
            //     int count = stockRtInfoMapper.insertBatch(info);
            //     if (count>0){
            //         log.info("当前时间：{},插入数据{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
            //     }else {
            //         log.error("当前时间：{},插入数据{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
            //     }
            // }).start();
            //方案2：多线程池：将采集任务放入线程池，线程池会自动分配线程，线程池的线程数量是固定的，不会因为任务数量而增加线程数量
            threadPoolTaskExecutor.execute(()->{
                    //1.1 获取数据地址
                    String url = stockInfoConfig.getMarketUrl()+String.join(",", list);
                    HttpEntity<Object> entity = new HttpEntity<>(headers);
                    //TODO 获取响应数据(直接获取响应数据)
                    String result = restTemplate.postForObject(url,entity,String.class);
                    //2 处理js数据
                    List<StockRtInfo> info = parserStockInfoUtil.parser4StockOrMarketInfo(result, ParseType.ASHARE);
                    log.info("采集的当前个股数据：{}",info);
                    //批量插入
                    if (CollectionUtils.isEmpty(info)) {
                        return;
                    }
                    // //3 保存
                    // int count = stockRtInfoMapper.insertBatch(info);
                    // if (count>0){
                    //     log.info("当前时间：{},插入数据{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
                    // }else {
                    //     log.error("当前时间：{},插入数据{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),info);
                    // }
            });
        }
        log.info("采集数据耗时：{}ms",System.currentTimeMillis()-startTime);

    }

    /**
     * 批量获取板块数据详情信息
     * https://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php
     */
    @Override
    public void getStockBlockInfo() {
        //1 从api获取数据
        //1.1 请求地址
        String url = stockInfoConfig.getBlockUrl();
        //1.2 请求头
        HttpHeaders headers = new HttpHeaders();
        //1.3 防盗链
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0");
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        //1.4 获取数据
        String result = restTemplate.postForObject(url, entity, String.class);
        if (StringUtils.isBlank(result)){
            return;
        }
        //2 处理js数据
        List<StockBlockRtInfo> infos = parserStockInfoUtil.parse4StockBlock(result);
        log.info("采集的当前板块数据：{}",infos);
        //批量插入
        if (CollectionUtils.isEmpty(infos)) {
            return;
        }
        //3 保存 使用线程分批保存
        //TODO AtomicInteger 是 Java 提供的一个原子整数类，适用于多线程环境下的计数操作
        AtomicInteger count = new AtomicInteger(0);
        Lists.partition(infos,20).forEach(list->{
            threadPoolTaskExecutor.execute(()->{
                stockBlockRtInfoMapper.insertBatch(list);
            });
            count.addAndGet(list.size());
        });
        // 获取最终的 count 值
        int finalCount = count.get();
        if (finalCount>0){
            log.info("当前时间：{},插入数据{}成功", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }else {
            log.error("当前时间：{},插入数据{}失败", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }

    }

}
